#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>




#include <sys/shm.h>
#include <sys/sem.h>
#include <stdlib.h>
#include <stdio.h>
#include <features.h>


#define    SIZE         (100)
#define    NUMOFREADER    (2)
char       buffer[SIZE];
char       infa='a';

union semun {
    int             val;
    struct          semid_ds *buf;
    unsigned short  *array;
};


/*returns -1 if faild and positive number if o.k*/
static int SetUpSemaphore(int _semID, int _val)
{
     union semun        semu;
     int                status;

     semu.val   =   _val;
     status=semctl(_semID,
     0,/*index in semaphore array*/
     SETVAL,/*prder ot setup*/
     semu);
     return status;
}

static int SemUnlock(int _sem_id )
{
    struct sembuf sem_b;
    sem_b.sem_num = 0;
    sem_b.sem_op = 1; /* 1 it meens increment the value by 1 */
    sem_b.sem_flg = SEM_UNDO;/*Flag for operation system*/

    if (semop(_sem_id, &sem_b, 1) == -1)
    {
        fprintf(stderr,"semaphore Unlock failed\n");
        return 1;
    }
    return 0;
}

static int SemLock(int _sem_id)
{
    struct sembuf sem_b;
    sem_b.sem_num = 0;
    sem_b.sem_op = -1; /* -1 it meens decrement the value by 1 */
    sem_b.sem_flg = SEM_UNDO;/*Flag for operation system*/

    if (semop(_sem_id, &sem_b,
    1/* Number of operatios per one system call on semaphore array (MAX 32)*/
    ) == -1)
    {
        fprintf(stderr,"\nsemaphore lock failed\n");
        return 1;
    }
    return 0;
}


void* ReaderProcess();
void* WriteProcess();
int main()
{
    pthread_t               write_thread;
    pthread_t               reade_thread[NUMOFREADER];
    key_t                   keyA;
    key_t                   keyB;
    key_t                   keyC;
    key_t                   keyD;
    key_t                   keyE;

    int                     semIDA;
    int                     semIDB;
    int                     semIDC;
    int                     semIDD;
    int                     semIDE;
    void*                   argW[3];
    void*                   argR1[6];
    void*                   argR2[6];
    int                     thr1 =1;
    int                     thr2 =2;
    int                     thr3 =3;
    int                     thr4 =4;



    /*Semaphore key for writing process A  */
    keyA = ftok("/home/seriygena/Documents/Projects/PtreadPingPong/src/pthreadpingpong.c",'A');


    /*Semaphore keys for reading process */
    keyB = ftok("/home/seriygena/Documents/Projects/PtreadPingPong/src/pthreadpingpong.c",'B');
    keyC = ftok("/home/seriygena/Documents/Projects/PtreadPingPong/src/pthreadpingpong.c",'C');
    keyD = ftok("/home/seriygena/Documents/Projects/PtreadPingPong/src/pthreadpingpong.c",'D');
    keyE = ftok("/home/seriygena/Documents/Projects/PtreadPingPong/src/pthreadpingpong.c",'E');

    semIDA = semget(keyA,1, 0666 | IPC_CREAT );


    semIDB = semget(keyB,1, 0666 | IPC_CREAT );
    semIDC = semget(keyC,1, 0666 | IPC_CREAT );
    semIDD = semget(keyD,1, 0666 | IPC_CREAT );
    semIDE = semget(keyE,1, 0666 | IPC_CREAT );


    SetUpSemaphore(semIDA,1);

    SetUpSemaphore(semIDB,0);
    SetUpSemaphore(semIDC,0);
    SetUpSemaphore(semIDD,0);
    SetUpSemaphore(semIDE,0);


    argW[0] = (void*)&semIDA;
    argW[1] = (void*)&semIDB;
    argW[2] = (void*)&semIDD;

    argR1[0] = (void*)&semIDA;
    argR1[1] = (void*)&semIDB;
    argR1[2] = (void*)&semIDC;
    argR1[3] = (void*)&semIDD;
    argR1[4] = (void*)&semIDE;


    argR2[0] = (void*)&semIDA;
    argR2[1] = (void*)&semIDB;
    argR2[2] = (void*)&semIDC;
    argR2[3] = (void*)&semIDD;
    argR2[4] = (void*)&semIDE;






        pthread_create( &write_thread   , NULL, WriteProcess ,argW);

        argR1[5] =  (void*)&thr1;
        pthread_create( &reade_thread[0], NULL, ReaderProcess,  argR1);
        argR2[5] =  (void*)&thr2;
        pthread_create( &reade_thread[1], NULL, ReaderProcess,  argR2);
       /* argR[5] =  (void*)&thr3;
        pthread_create( &reade_thread[2], NULL, ReaderProcess,  argR);
        argR[5] =  (void*)&thr4;
        pthread_create( &reade_thread[3], NULL, ReaderProcess,  argR);*/

     pthread_join(reade_thread[0], NULL);
     pthread_join(reade_thread[1], NULL);
  /*   pthread_join(reade_thread[2], NULL);
     pthread_join(reade_thread[3], NULL);*/

     semctl(semIDA,0, IPC_RMID);
     semctl(semIDB,0, IPC_RMID);
     semctl(semIDC,0, IPC_RMID);
     semctl(semIDD,0, IPC_RMID);
     semctl(semIDE,0, IPC_RMID);
    return 0;
}

void* WriteProcess(void* _argW)
{
    int        i;
    int        loopw = 0;
    int        semIDA =*(((int**)_argW)[0]);
    int        semIDB =*(((int**)_argW)[1]);
    int        semIDD =*(((int**)_argW)[2]);


  while(1)
  {

    /*Enter and lock its semaphore - sem.val == 0*/
    SemLock(semIDA);

    /*Open 1st semaphore to readers, after all theread enters it get value 0 -locked*/
     SetUpSemaphore(semIDB,NUMOFREADER);
    /*Start to write*/
    for(i=0; i<SIZE; ++i)
    {
        *(buffer+i) = infa;
    }
    ++infa;

    /*After it finished to write it open the semaphore D*/
     SetUpSemaphore(semIDD,NUMOFREADER);
    ++loopw;
  }
     return (void*)0;
}

void* ReaderProcess(void* _argR)
{
    char c;
    int         counter=0;
    int         i=0;
    int        loopr = 0;

    int        thrID =  *(((int**)_argR)[5]);
    int        semIDA = *(((int**)_argR)[0]);
    int        semIDB = *(((int**)_argR)[1]);
    int        semIDC = *(((int**)_argR)[2]);
    int        semIDD = *(((int**)_argR)[3]);
    int        semIDE = *(((int**)_argR)[4]);



    while(1)
    {


    /*Reader threads will try to lock the semaphore*/

      SemLock(semIDB);
      ++counter;
      if(counter == NUMOFREADER) /*All thread passed the semaphore B*/
      {
          SetUpSemaphore(semIDC,NUMOFREADER);
          counter = 0;
      }

       SemLock(semIDC);/*This semaphores unlocket  when all process already passed the B semaphore*/
       SemLock(semIDD);/*Semapore will unlocked  D by write thread when write will be ready*/

       printf( "\nThread %d Reading:  ", thrID);
       for(i=0; i<SIZE; ++i)
       {
          c =  *(buffer+i);

       }
       printf("MESSAGE was readed by thread %d %c",thrID, c);
       fflush(stdout);
       ++counter;

       if(counter == NUMOFREADER)
       {
           SetUpSemaphore(semIDE,NUMOFREADER);/*All  threads finished reading*/

           counter= 0;
       }
       SemLock(semIDE);
       SemUnlock(semIDA);/*Semaphore A can be unlocked*/
       ++loopr;
    }
       return (void*)0;
}











